trait Draw {
    fn draw(&self) -> String;
}

impl Draw for u8 {
    fn draw(&self) -> String {
        format!("u8: {}", *self)
    }
}

impl Draw for f64 {
    fn draw(&self) -> String {
        format!("f64: {}", *self)
    }
}

struct Button {
    pub width: u32,
    pub height: u32,
    pub label: String,
}

struct SelectBox {
    width: u32,
    height: u32,
    options: Vec<String>,
}

impl Draw for SelectBox {
    fn draw(&self) -> String {
        format!("{} {} {:?}", self.height, self.width, self.options)
    }
}

impl Draw for Button {
    fn draw(&self) -> String {
        format!("{} {} {}", self.height, self.width, self.label)
    }
}

impl<T: Draw + ?Sized> Draw for Box<T> {
    fn draw(&self) -> String {
        T::draw(self)
    }
}

struct Screen<T: Draw> {
    pub components: Vec<T>,
}

impl<T> Screen<T>
where
    T: Draw,
{
    pub fn run(&self) {
        for component in self.components.iter() {
            println!("{}", component.draw());
        }
    }
}

// 泛型是在编译期完成处理的：编译器会为每一个泛型参数对应的具体类型生成一份代码，这种方式是静态分发(static dispatch)，因为是在编译期完成的，对于运行期性能完全没有任何影响。
// 动态分发(dynamic dispatch)，在这种情况下，直到运行时，才能确定需要调用什么方法。 代码中的关键字 dyn 正是在强调这一“动态”的特点。
#[cfg(test)]
mod tests {

    // draw1 函数的参数是 Box<dyn Draw> 形式的特征对象，该特征对象是通过 Box::new(x) 的方式创建的
    // draw2 函数的参数是 &dyn Draw 形式的特征对象，该特征对象是通过 &x 的方式创建的
    // dyn 关键字只用在特征对象的类型声明上，在创建时无需使用 dyn
    // 若 T 实现了 Draw 特征， 则调用该函数时传入的 Box<T> 可以被隐式转换成函数参数签名中的 Box<dyn Draw>

    use crate::basex::structx::trait_obj::{Button, Draw};

    // Deref 解引用
    fn draw1(x: Box<dyn Draw>) {
        // 由于实现了 Deref 特征，Box 智能指针会自动解引用为它所包裹的值，然后调用该值对应的类型上定义的 `draw` 方法
        println!("dyn:{:?}", x.draw());
    }

    fn draw2(x: &dyn Draw) {
        println!("&dyn:{}", x.draw());
    }

    #[test]
    fn trans() {
        let x = 1.1f64;
        // do_something(&x);
        let y = 8u8;

        // x 和 y 的类型 T 都实现了 `Draw` 特征，因为 Box<T> 可以在函数调用时隐式地被转换为特征对象 Box<dyn Draw>
        // 基于 x 的值创建一个 Box<f64> 类型的智能指针，指针指向的数据被放置在了堆上
        // 基于 y 的值创建一个 Box<u8> 类型的智能指针
        draw1(Box::new(x));
        draw1(Box::new(y));

        draw2(&x);
        draw2(&y);
        println!("---------");
        draw1(Box::new(Button {
            width: 1,
            height: 12,
            label: "sd".to_string(),
        }));
    }
}

struct ScreeSize<T: Draw> {
    pub components: Vec<Box<T>>,
}

impl<T> ScreeSize<T>
where
    T: Draw,
{
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}

#[cfg(test)]
mod trait_trans {
    use crate::basex::structx::trait_obj::{Button, Draw, Screen, SelectBox};

    #[test]
    fn run() {
        // 只要它实现了 Draw 特征，就能通过 Box::new 包装成 Box<dyn Draw> 特征对象，然后被渲染在屏幕上
        let select_box = Box::new(SelectBox {
            width: 75,
            height: 10,
            options: vec![
                String::from("Yes"),
                String::from("Maybe"),
                String::from("No"),
            ],
        });
        let button = Box::new(Button {
            width: 50,
            height: 10,
            label: String::from("OK"),
        });
        let screen = Screen::<Box<dyn Draw>> {
            components: vec![select_box, button],
        };
        screen.run()
    }

    #[test]
    fn box_tran() {
        let sl = Box::new(SelectBox {
            width: 75,
            height: 10,
            options: vec![
                String::from("Yes"),
                String::from("Maybe"),
                String::from("No"),
            ],
        });

        let bu = Box::new(Button {
            width: 50,
            height: 10,
            label: String::from("OK"),
        });
        // let size :ScreeSize<dyn Draw>= ScreeSize { components: vec![bu, sl] };
        // size.run();
    }
}
